package org.example.app.config.aot;

import com.baomidou.mybatisplus.annotation.IEnum;
import com.baomidou.mybatisplus.core.MybatisParameterHandler;
import com.baomidou.mybatisplus.core.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler;
import com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
import com.baomidou.mybatisplus.extension.handlers.GsonTypeHandler;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.cache.decorators.FifoCache;
import org.apache.ibatis.cache.decorators.LruCache;
import org.apache.ibatis.cache.decorators.SoftCache;
import org.apache.ibatis.cache.decorators.WeakCache;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.BaseStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.javassist.util.proxy.ProxyFactory;
import org.apache.ibatis.javassist.util.proxy.RuntimeSupport;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl;
import org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl;
import org.apache.ibatis.logging.log4j2.Log4j2Impl;
import org.apache.ibatis.logging.nologging.NoLoggingImpl;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.scripting.defaults.RawLanguageDriver;
import org.apache.ibatis.scripting.xmltags.XMLLanguageDriver;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeSet;
import java.util.stream.Stream;

public class MybaitsRuntimeHintsRegistrar implements RuntimeHintsRegistrar {

    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        Stream.of(RawLanguageDriver.class,
                // TODO 增加了MybatisXMLLanguageDriver.class
                XMLLanguageDriver.class, MybatisXMLLanguageDriver.class,
                RuntimeSupport.class,
                ProxyFactory.class,
                Slf4jImpl.class,
                Log.class,
                JakartaCommonsLoggingImpl.class,
                Log4j2Impl.class,
                Jdk14LoggingImpl.class,
                StdOutImpl.class,
                NoLoggingImpl.class,
                SqlSessionFactory.class,
                PerpetualCache.class,
                FifoCache.class,
                LruCache.class,
                SoftCache.class,
                WeakCache.class,
                // TODO 增加了MybatisSqlSessionFactoryBean.class
                SqlSessionFactoryBean.class, MybatisSqlSessionFactoryBean.class,
                ArrayList.class,
                HashMap.class,
                TreeSet.class,
                HashSet.class
        ).forEach(x -> hints.reflection().registerType(x, MemberCategory.values()));
        Stream.of(
                "org/apache/ibatis/builder/xml/*.dtd",
                "org/apache/ibatis/builder/xml/*.xsd"
        ).forEach(hints.resources()::registerPattern);

        hints.serialization().registerType(SerializedLambda.class);
        hints.serialization().registerType(SFunction.class);
        hints.serialization().registerType(java.lang.invoke.SerializedLambda.class);
        hints.reflection().registerType(SFunction.class);
        hints.reflection().registerType(SerializedLambda.class);
        hints.reflection().registerType(java.lang.invoke.SerializedLambda.class);

        hints.proxies().registerJdkProxy(StatementHandler.class);
        hints.proxies().registerJdkProxy(Executor.class);
        hints.proxies().registerJdkProxy(ResultSetHandler.class);
        hints.proxies().registerJdkProxy(ParameterHandler.class);

        // hints.reflection().registerType(MybatisPlusInterceptor.class);
        hints.reflection().registerType(AbstractWrapper.class, MemberCategory.values());
        hints.reflection().registerType(LambdaQueryWrapper.class, MemberCategory.values());
        hints.reflection().registerType(LambdaUpdateWrapper.class, MemberCategory.values());
        hints.reflection().registerType(UpdateWrapper.class, MemberCategory.values());
        hints.reflection().registerType(QueryWrapper.class, MemberCategory.values());

        hints.reflection().registerType(BoundSql.class, MemberCategory.DECLARED_FIELDS);
        hints.reflection().registerType(RoutingStatementHandler.class, MemberCategory.DECLARED_FIELDS);
        hints.reflection().registerType(BaseStatementHandler.class, MemberCategory.DECLARED_FIELDS);
        hints.reflection().registerType(MybatisParameterHandler.class, MemberCategory.DECLARED_FIELDS);


        hints.reflection().registerType(IEnum.class, MemberCategory.INVOKE_PUBLIC_METHODS);
        // register typeHandler
        hints.reflection().registerType(CompositeEnumTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
        hints.reflection().registerType(FastjsonTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
        hints.reflection().registerType(GsonTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
        hints.reflection().registerType(JacksonTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
        hints.reflection().registerType(MybatisEnumTypeHandler.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
    }
}
